package xiaoa.java.netty;

import java.net.URI;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;

import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelPipeline;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.HttpRequest;
import io.netty.handler.codec.http.HttpRequestEncoder;
import io.netty.handler.codec.http.HttpResponseDecoder;
import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.GenericFutureListener;

/**
 * http工具类
 * @author xiaoa
 * @date 2017年8月18日 下午11:02:30
 * @version V1.0
 *
 */
public abstract class HttpClient {
	
	/**
	 * 第一个通道名字
	 */
	public static String FIRST_HANDLE_NAME = "FIRST_HANDLE_NAME";


	/**
	 * 最后的数据结果handler
	 */
	public static String RESULT_HANDLER = "RESULT_HANDLER";


	/**
	 * 发起请求
	 * @Title: doRequest
	 * @param request
	 * @return
	 * @throws Exception
	 * @author xiaoa
	 */
	abstract HttpResponseResult  doRequest(HttpRequest request)throws Exception;
	
	
	/**
	 * 发起请求
	 * @Title: doRequest
	 * @param request
	 * @return
	 * @throws Exception
	 * @author xiaoa
	 */
	abstract HttpResponseResult  doRequest(HttpRequest request , int socketTimeOut )throws Exception;
	
	/**
	 * 发起请求
	 * @Title: doRequest
	 * @param request
	 * @param socketTimeOut
	 * @param connTimeOut
	 * @param handlerMap
	 * @return
	 * @throws Exception
	 * @author xiaoa
	 */
	abstract HttpResponseResult doRequest(final HttpRequest request, final int socketTimeOut , Map<String, String> handerMap) throws Exception ;

	/**
	 * 发起请求
	 * @Title: doRequest
	 * @param request
	 * @param socketTimeOut
	 * @param handlerMap
	 * @return
	 * @throws Exception
	 * @author xiaoa
	 */
	abstract HttpResponseResult doRequest(final HttpRequest request , Map<String, String> handerMap) throws Exception ;

	
	/**
	 * 释放连接
	 * @Title: release
	 * @param channel
	 * @param url
	 * @throws Exception
	 * @author xiaoa
	 */
	abstract void release(Channel  channel , URI uri)throws Exception;


	/**
	 * 释放并且关闭连接
	 * @param channel
	 * @param uri
	 * @throws Exception
     */
	abstract void releaseAndClose(Channel  channel , URI uri)throws Exception;



	/**
	 * 初始化请求handler
	 * @Title: initFillHandler
	 * @param pipeline
	 * @author xiaoa
	 */
	protected  void initFillHandler(ChannelPipeline pipeline){
		
		// 将多个消息转为一个full
		pipeline.addLast(FIRST_HANDLE_NAME , new HttpObjectAggregator(Integer.MAX_VALUE));
		
		// 添加请求编码
		pipeline.addLast(new HttpRequestEncoder());
		
		// 添加请求解码
		pipeline.addLast(new HttpResponseDecoder());
		
	}
	
	/**
	 * 发起请求，提交数据
	 * @Title: doReuqest
	 * @param fu
	 * @return
	 * @throws Exception
	 * @author xiaoa
	 */
	protected final <F> HttpResponseResult doPutReuqest(final Future<F> fu,final HttpRequest request, final int socketTimeOut ,final Map<String, String> handerMap)throws Exception{
		
		final HttpResponseResult  result = new HttpResponseResult();
		
	    final URI uri =  new URI(request.uri());
		
		if (uri == null || uri.toString().isEmpty()){
			throw new RuntimeException(" uri is null");
		}
		
		final HttpClient  thisClient = this;
		
		 fu.addListener(new GenericFutureListener<Future<F>>() {

			@Override
			public void operationComplete(Future<F> future) throws Exception {
				
				// 完成连接
				try {
					result.completeConnect();
					
					Channel  channel = null;
					
					if (future instanceof ChannelFuture){
						channel = ((ChannelFuture)future).channel();
					}else if (future instanceof Future){
						channel = (Channel)future.get();
					}else {
						result.exceptionClose(new RuntimeException(future.getClass() +  " un Support"));
					}
					// 如果连接失败
					if (!future.isSuccess()){
						try {
							// 关闭连接
							release(channel, uri);
							
						} finally {
							result.exceptionClose(future.cause());
						}
						return ;
					}
					
					// 判断是否通道是否初始化过
				    if (!isInit(channel.pipeline())){
				    	
				    	Log.debug(channel + " pipeline init ");
				    	initFillHandler(channel.pipeline());
				    }	
					
					Map<String, String> handerMapTemp = handerMap;
					
					// 如果有连接超时
					if (socketTimeOut > 0){
						
						if (handerMapTemp == null){
							handerMapTemp = new HashMap<>();
						}
						
						handerMapTemp.put(HttpHandlerUtils.HTTP_SOCKET_TIMEOUT, socketTimeOut + "");
					}
					
					// 设置超时时间
					// 填充请求头
					HttpHandlerUtils.doFill(request, handerMapTemp);
					
					// 填充初始化处理handler
					HttpHandlerUtils.doFillInitHander(channel.pipeline(),request ,  uri.toString());

					if (channel.pipeline().get(RESULT_HANDLER) != null){
						channel.pipeline().remove(RESULT_HANDLER);
					}

					HttpResponseHandler  resultHandler =  new HttpResponseHandler(result , thisClient , uri);
					// 绑定hand
					channel.pipeline().addLast(RESULT_HANDLER,resultHandler );

					// 打印hander
					if (Log.isDebug()){
						Iterator<Entry<String, ChannelHandler>>  it = channel.pipeline().iterator();
						
						StringBuilder  log = new StringBuilder(channel.toString());
						
						while(it.hasNext()){
							
							Entry<String, ChannelHandler>  e = it.next();
							
							log.append("[" + e.getKey() + "](" + e.getValue().getClass().getSimpleName() + ")===>");
						}

						log.append("\n");
						
						Log.debug(log);
						
					}
				
					// 发送数据
					channel.writeAndFlush(request).addListener(new GenericFutureListener<ChannelFuture>() {

						@Override
						public void operationComplete(ChannelFuture future) throws Exception {
							
							Log.debug(future.channel().toString() + ":发送数据包成功" + future.isSuccess());
							
							if (!future.isSuccess()){
								try {
									// 关闭连接
									release(future.channel(), uri);
									
								} finally {
									result.exceptionClose(future.cause());
								}
								return ;
							}

						}
					});
					
				} catch (Exception e) {
					result.exceptionClose(e);
				}finally {
					fu.removeListener(this);
				}
				
				
			}
		});
		
		result.waitResult(0);
		
		// 如果失败
		if (!result.isSucc()){
			
			if(result.getException() != null){
				throw new RuntimeException(result.getException());
			}
		}
		
		return result;
		
	}
	
	
	
	/**
	 * 判断通道是否初始化过
	 * @Title: isInit
	 * @param p
	 * @return
	 * @author xiaoa
	 */
	protected static boolean isInit(ChannelPipeline  p){

		return p.get(FIRST_HANDLE_NAME) != null;
 		
	}

	
	/**
	 * 获取端口
	 * @Title: getPort
	 * @param uri
	 * @return
	 * @author xiaoa
	 */
	protected static int getPort(URI uri){
		
		int port = uri.getPort();

		if (port == -1){
			if(uri.toString().toLowerCase().startsWith("http:")){
				port = 80;
			}else{
				port = 443;
			}
		}
		
		return port;
	}

	
}
